#include <algorithm>
#include "rasterizer.hpp"
#include <opencv2/opencv.hpp>
#include <math.h>

// 把顶点的位置vector 加载到buffer，返回对应的bufferid，也就是map的key
rst::pos_buf_id rst::rasterizer::load_positions(const std::vector<Eigen::Vector3f> &positions){
    int id = get_next_id();
    pos_buf.emplace(id, positions);

    return {id};
}

// 把顶点的索引vector 加载到buffer，返回对应的bufferid，也就是map的key
rst::ind_buf_id rst::rasterizer::load_indices(const std::vector<Eigen::Vector3i> &indices){
    int id = get_next_id();
    ind_buf.emplace(id, indices);

    return {id};
}

// 把顶点的颜色vector 加载到buffer，返回对应的bufferid，也就是map的key
rst::col_buf_id rst::rasterizer::load_colors(const std::vector<Eigen::Vector3f> &cols){
    int id = get_next_id();
    col_buf.emplace(id, cols);

    return {id};
}

// 把顶点的法线vector 加载到buffer，返回对应的bufferid，也就是map的key
rst::col_buf_id rst::rasterizer::load_normals(const std::vector<Eigen::Vector3f> &normals){
    int id = get_next_id();
    nor_buf.emplace(id, normals);

    normal_id = id;

    return {id};
}

// Bresenham算法，画直线
void rst::rasterizer::draw_line(Eigen::Vector3f begin, Eigen::Vector3f end){
    auto x1 = begin.x();
    auto y1 = begin.y();
    auto x2 = end.x();
    auto y2 = end.y();

    Eigen::Vector3f line_color = {255, 255, 255};

    int x, y, dx, dy, dx1, dy1, px, py, xe, ye, i;

    dx = x2 - x1;
    dy = y2 - y1;
    dx1 = fabs(dx);
    dy1 = fabs(dy);
    px = 2 * dy1 - dx1;
    py = 2 * dx1 - dy1;

    if (dy1 <= dx1)
    {
        if (dx >= 0)
        {
            x = x1;
            y = y1;
            xe = x2;
        }
        else
        {
            x = x2;
            y = y2;
            xe = x1;
        }
        Eigen::Vector2i point = Eigen::Vector2i(x, y);
        set_pixel(point, line_color);
        for (i = 0; x < xe; i++)
        {
            x = x + 1;
            if (px < 0)
            {
                px = px + 2 * dy1;
            }
            else
            {
                if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0))
                {
                    y = y + 1;
                }
                else
                {
                    y = y - 1;
                }
                px = px + 2 * (dy1 - dx1);
            }
            //            delay(0);
            Eigen::Vector2i point = Eigen::Vector2i(x, y);
            set_pixel(point, line_color);
        }
    }
    else
    {
        if (dy >= 0)
        {
            x = x1;
            y = y1;
            ye = y2;
        }
        else
        {
            x = x2;
            y = y2;
            ye = y1;
        }
        Eigen::Vector2i point = Eigen::Vector2i(x, y);
        set_pixel(point, line_color);
        for (i = 0; y < ye; i++)
        {
            y = y + 1;
            if (py <= 0)
            {
                py = py + 2 * dx1;
            }
            else
            {
                if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0))
                {
                    x = x + 1;
                }
                else
                {
                    x = x - 1;
                }
                py = py + 2 * (dx1 - dy1);
            }
            //            delay(0);
            Eigen::Vector2i point = Eigen::Vector2i(x, y);
            set_pixel(point, line_color);
        }
    }
}


auto to_vec4(const Eigen::Vector3f &v3, float w = 1.0f){
    return Eigen::Vector4f(v3.x(), v3.y(), v3.z(), w);
}

static bool insideTriangle( int x, int y, const Vector4f *_v){
    Vector3f v[3];
    for (int i = 0; i < 3; i++)
        v[i] = {_v[i].x(), _v[i].y(), 1.0};
    Vector3f f0, f1, f2;
    f0 = v[1].cross(v[0]);
    f1 = v[2].cross(v[1]);
    f2 = v[0].cross(v[2]);
    Vector3f p(x, y, 1.);
    if ((p.dot(f0) * f0.dot(v[2]) > 0) && (p.dot(f1) * f1.dot(v[0]) > 0) && (p.dot(f2) * f2.dot(v[1]) > 0))
        return true;
    return false;
}

//计算某点 对于某个三角形三点的 重心坐标
static std::tuple<float, float, float> computeBarycentric2D(float x, float y, const Vector4f *v){
    float c1 = (x * (v[1].y() - v[2].y()) + (v[2].x() - v[1].x()) * y + v[1].x() * v[2].y() - v[2].x() * v[1].y()) / (v[0].x() * (v[1].y() - v[2].y()) + (v[2].x() - v[1].x()) * v[0].y() + v[1].x() * v[2].y() - v[2].x() * v[1].y());
    float c2 = (x * (v[2].y() - v[0].y()) + (v[0].x() - v[2].x()) * y + v[2].x() * v[0].y() - v[0].x() * v[2].y()) / (v[1].x() * (v[2].y() - v[0].y()) + (v[0].x() - v[2].x()) * v[1].y() + v[2].x() * v[0].y() - v[0].x() * v[2].y());
    float c3 = (x * (v[0].y() - v[1].y()) + (v[1].x() - v[0].x()) * y + v[0].x() * v[1].y() - v[1].x() * v[0].y()) / (v[2].x() * (v[0].y() - v[1].y()) + (v[1].x() - v[0].x()) * v[2].y() + v[0].x() * v[1].y() - v[1].x() * v[0].y());
    return {c1, c2, c3};
}

// 画图。处理三角形列表， 执行MVP变换 和 Viewport 变换
void rst::rasterizer::draw(std::vector<Triangle *> &TriangleList){
    
    float f1 = (50 - 0.1) / 2.0;
    float f2 = (50 + 0.1) / 2.0;

    Eigen::Matrix4f mvp = projection * view * model;
    for (const auto &t : TriangleList)
    {
        Triangle newtri = *t;

        std::array<Eigen::Vector4f, 3> mm{
            (view * model * t->v[0]),
            (view * model * t->v[1]),
            (view * model * t->v[2])
        };

        std::array<Eigen::Vector3f, 3> viewspace_pos;

        std::transform(mm.begin(), mm.end(), viewspace_pos.begin(), [](auto &v) {
            return v.template head<3>();
        });

        Eigen::Vector4f v[] = {
            mvp * t->v[0],
            mvp * t->v[1],
            mvp * t->v[2]};
        //Homogeneous division
        for (auto &vec : v)
        {
            vec.x() /= vec.w();
            vec.y() /= vec.w();
            vec.z() /= vec.w();
        }

        Eigen::Matrix4f inv_trans = (view * model).inverse().transpose();
        Eigen::Vector4f n[] = {
            inv_trans * to_vec4(t->normal[0], 0.0f),
            inv_trans * to_vec4(t->normal[1], 0.0f),
            inv_trans * to_vec4(t->normal[2], 0.0f)};

        //Viewport transformation
        for (auto &vert : v)
        {
            vert.x() = 0.5 * width * (vert.x() + 1.0);
            vert.y() = 0.5 * height * (vert.y() + 1.0);
            vert.z() = vert.z() * f1 + f2;
        }

        for (int i = 0; i < 3; ++i)
        {
            //screen space coordinates
            newtri.setVertex(i, v[i]);
        }

        for (int i = 0; i < 3; ++i)
        {
            //view space normal
            newtri.setNormal(i, n[i].head<3>());
        }

        newtri.setColor(0, 148, 121.0, 92.0);
        newtri.setColor(1, 148, 121.0, 92.0);
        newtri.setColor(2, 148, 121.0, 92.0);

        // Also pass view space vertice position
        rasterize_triangle(newtri, viewspace_pos);
    }
}


// 分别是三维的插值 和 二维的插值

//三维的插值可以用来插值 颜色、法线、位置
static Eigen::Vector3f interpolate(float alpha, float beta, float gamma, const Eigen::Vector3f &vert1, const Eigen::Vector3f &vert2, const Eigen::Vector3f &vert3, float weight)
{
    return (alpha * vert1 + beta * vert2 + gamma * vert3) / weight;
}

// 二维的用来差值纹理坐标 uv
static Eigen::Vector2f interpolate(float alpha, float beta, float gamma, const Eigen::Vector2f &vert1, const Eigen::Vector2f &vert2, const Eigen::Vector2f &vert3, float weight)
{
    auto u = (alpha * vert1[0] + beta * vert2[0] + gamma * vert3[0]);
    auto v = (alpha * vert1[1] + beta * vert2[1] + gamma * vert3[1]);

    u /= weight;
    v /= weight;

    return Eigen::Vector2f(u, v);
}

void rst::rasterizer::rasterize_triangle(const Triangle &t, const std::array<Eigen::Vector3f, 3> &view_pos ){
    auto v = t.toVector4();
    // 先吧三角形轉換成齊次坐標

    // get bounding box of triangle
    float min_x = std::min( v[0][0], std::min(v[1][0], v[2][0]) );
    float max_x = std::max( v[0][0], std::max(v[1][0], v[2][0]) );
    float min_y = std::min( v[0][1], std::max(v[1][1], v[2][1]) );
    float max_y = std::max( v[0][1], std::max(v[1][1], v[2][1]) );

    min_x = std::floor(min_x);
    max_x = std::ceil(max_x);
    min_y = std::floor(min_y);
    max_y = std::ceil(max_y);

    //遍歷bounding box中的所有像素
    for ( int x=min_x; x<max_x; x++ ){
        for ( int y=min_y; y<max_y; y++ ){
            if ( insideTriangle(x, y, t.v)){
                //depth test
                float min_depth = FLT_MAX;

                //計算重心坐標
                auto [alpha, beta, gamma] = computeBarycentric2D(x, y, t.v);

                bool is_edge = ( alpha < 0.01f || beta < 0.01f || gamma < 0.01f );

                float Z = 1.0 / ( alpha/v[0].w() + beta/v[1].w() + gamma/v[2].w() );
                float zp = alpha * v[0].z()/v[0].w() + beta * v[1].z()/v[1].w() + gamma * v[2].z()/v[2].w();
                zp *= Z;

                //數值越小，說明離鏡頭越近，那就能看見，那麼就要更新depth buffer 和 frame buffer
                min_depth = std::min(min_depth, zp);
                if (min_depth < depth_buf[ get_index(x, y) ]){
                    auto interpolated_color = alpha * t.color[0] + beta * t.color[1] + gamma * t.color[2];
                    auto interpolated_normal = alpha * t.normal[0] + beta * t.normal[1] + gamma * t.normal[2];
                    auto interpolated_tex_coords = alpha * t.tex_coords[0] + beta * t.tex_coords[1] + gamma * t.tex_coords[2];
                    auto interpolated_shading_coords = alpha * view_pos[0] + beta * view_pos[1] + gamma * view_pos[2];

                    fragment_shader_payload payload(
                        interpolated_color, 
                        interpolated_normal.normalized(), 
                        interpolated_tex_coords, 
                        texture ? &*texture : nullptr
                    );

                    Eigen::Vector3f pixel_color;
                    if ( is_edge ){
                        pixel_color = Eigen::Vector3f(255, 255, 255);
                    } else {
                        pixel_color = fragment_shader(payload);
                    }

                    depth_buf[ get_index(x, y) ] = min_depth;
                    Eigen::Vector2i point(x,y);
                    set_pixel( point, pixel_color );
                }

            }
        }
    }

}

void rst::rasterizer::set_model(const Eigen::Matrix4f &m){
    model = m;
}

void rst::rasterizer::set_view(const Eigen::Matrix4f &v){
    view = v;
}

void rst::rasterizer::set_projection(const Eigen::Matrix4f &p){
    projection = p;
}

void rst::rasterizer::clear(rst::Buffers buff){
    if ( (buff & rst::Buffers::Color) == rst::Buffers::Color ){
        //顏色設置為黑色
        std::fill( frame_buf.begin(), frame_buf.end(), Eigen::Vector3f{0, 0, 0} );
    }
    if ( (buff & rst::Buffers::Color) == rst::Buffers::Color ){
        //深度設置為無限遠
        std::fill( depth_buf.begin(), depth_buf.end(), std::numeric_limits<float>::infinity() );
    }
}

rst::rasterizer::rasterizer(int w, int h) : width(w), height(h){
    frame_buf.resize(w * h);
    depth_buf.resize(w * h);

    texture = std::nullopt;
}

int rst::rasterizer::get_index(int x, int y){
    return ( height - y ) * width + x;
}

void rst::rasterizer::set_pixel(const Vector2i &point, const Eigen::Vector3f &color){
    int ind = (height - point.y()) * width + point.x();
    frame_buf[ind] = color;
}

// 接受一個函數對象，函數的返回值是Eigen::Vector3f， 參數是一個fragment_shader_payload
void rst::rasterizer::set_vertex_shader(std::function<Eigen::Vector3f(vertex_shader_payload)> vert_shader){
    vertex_shader = vert_shader;
}

void rst::rasterizer::set_fragment_shader(std::function<Eigen::Vector3f(fragment_shader_payload)> frag_shader){
    fragment_shader = frag_shader;
}